/*** V20-D2021-09-12		
	决策：
	1- 为了 框架上的 “顺畅” ，增加一个 YModem Buf 存放接收帧
	2- 受限与RAM资源限制，默认使用 128固定长度传输数据 
		而非使用1024. // 若实现了Malloc 可以不受限于此
*/
#include "YModemSlave.h"

/*** Interior Define */
// ===
#define RYM_RPKG_SN_SHOW_OC 	1 // 调试-显示接收包 SN

#define LLOG_ERR
// #define LLOG_MARK
#define LLOG
#include "llog.h"

#ifdef	CFG_SYS_MALLOC

#define L_MALLOC 	MALLOC
#define L_FREE 		MFREE

#else

#include <stdlib.h>
#define L_MALLOC(A) malloc(A)
#define L_FREE(A)	free(A)

#endif

#define YM_S_START	0x00
#define YM_S_IDLE	0xFE
#define YM_S_EXIT	0xFF
/*** Interior Struct */
// typedef int (*rym_callback)(tYM_Slave* ctx, uint16_t len, uint8_t *buf);

struct CC_YModem_Slave_StateMachine{

	uint8_t 	stage;
	uint8_t 	*pBuf;	 
	uint16_t 	BufSz;
	
	uint32_t 	RemainNum;	
	uint8_t  	Flag; 
	uint8_t  	PkgSnCnt; 
	uint16_t 	WaitSecCnt;	// 等待握手的时间
	uint32_t 	MarkTick;

	// N-处理节点 回调函数
	int (*pProcBeginPkg)(tYM_Slave* ctx, uint16_t len, uint8_t *buf);	
    int (*pProcDataPkg)(tYM_Slave* ctx, uint16_t len, uint8_t *buf);	
    int (*pProcEndPkg)(tYM_Slave* ctx, uint16_t len, uint8_t *buf);	
	int (*pExit)(tYM_Slave* ctx, uint16_t len, uint8_t *buf);			
	
	int (*pSend)(uint16_t Len,uint8_t* pPkg);
		
};

/*** Global Data */

/*** Local Function Define*/

//====

#ifdef CFG_YMODEM_SLAVE
void YM_Slave_Show(tYM_Slave* pObj){
	
	LLOG("stage		=[%x]\n",pObj->stage	);
	LLOG("pBuf		=[%x]\n",pObj->pBuf		);
	LLOG("BufSz		=[%x]\n",pObj->BufSz	);
	LLOG("RemainNum	=[%x]\n",pObj->RemainNum);	
	LLOG("Flag	    =[%x]\n",pObj->Flag);	
	LLOG("PkgSnCnt 	=[%x]\n",pObj->PkgSnCnt	); 
	LLOG("WaitSecCnt=[%x]\n",pObj->WaitSecCnt);
	LLOG("MarkTick	=[%x]\n",pObj->MarkTick	);

	LLOG("pProcDataPkg	=[%x]\n",pObj->pProcDataPkg	);
	LLOG("pProcBeginPkg	=[%x]\n",pObj->pProcBeginPkg);
	LLOG("pProcEndPkg	=[%x]\n",pObj->pProcEndPkg	);
	LLOG("pExit			=[%x]\n",pObj->pExit		);
	LLOG("pSend			=[%x]\n",pObj->pSend		);

}

tYM_Slave* YM_Slave_SCreate(uint32_t BufSz){
	
	tYM_Slave* ptmp=NULL;
	uint8_t* pBuf;
	uint32_t AllocSz;
	uint32_t ObjSz;
	
	ObjSz=sizeof(tYM_Slave);
	AllocSz=(ObjSz&0x03)?4:0;
	AllocSz+=ObjSz&(~0x03);

	pBuf = L_MALLOC(AllocSz+BufSz);
	if(pBuf == NULL){ LLOG_ERR(); return ptmp; }
	
	MEMSET(pBuf,0,AllocSz+BufSz);
	ptmp=(tYM_Slave*)&pBuf[0];	
	// 
	ptmp->BufSz	= BufSz;
	ptmp->pBuf	= &pBuf[AllocSz];
	ptmp->stage	= YM_S_IDLE;	
	
	ptmp->Flag	= 0;	
	
	return ptmp;
}

int YM_Slave_BindCallback(tYM_Slave* pObj,const tYMS_CallBackAPI* pAPI){
	//
	int ret = RET_ERR;
	if(pObj == NULL) {  LLOG_ERR(); return ret; }
	if(pAPI == NULL) {  LLOG_ERR(); return ret; }
	ret = RET_OK;
	
	pObj->pProcBeginPkg = pAPI->pProcBeginPkg;
	pObj->pProcDataPkg  = pAPI->pProcDataPkg;
	pObj->pProcEndPkg   = pAPI->pProcEndPkg;
	pObj->pExit 		= pAPI->pExit;
	
	pObj->Flag	|= 0x01;
	
	return ret;
}

int YM_Slave_Set_WaitSec(tYM_Slave* pObj,uint32_t WaitSec){
	// 等待 master 响应时间
	int ret=RET_ERR;
	
	if(pObj==NULL){
		LLOG_ERR();
		return ret;
	}
	ret=RET_OK;
	//
	pObj->WaitSecCnt = WaitSec;
	
	return ret;
}

int YM_Slave_Set_RemainNum(tYM_Slave* pObj,uint32_t FileByteSz){
	// 等待 master 响应时间
	int ret=RET_ERR;
	
	if(pObj==NULL){
		LLOG_ERR();
		return ret;
	}
	ret=RET_OK;
	//
	pObj->RemainNum = FileByteSz;
	
	return ret;
}

int YM_Slave_BindSend(tYM_Slave* pObj,int (*pFunc)(uint16_t,uint8_t*)){
	//
	int ret=RET_ERR;
	if(pObj == NULL) {  LLOG_ERR(); return ret; }
	ret=RET_OK;
	//
	pObj->pSend = pFunc;
	pObj->Flag	|= 0x02;
	
	return ret;
}

int YM_Slave_StateCtrl(tYM_Slave *pObj,uint8_t OnOff){ // 0==off 1==on 
	int ret=RET_ERR;
	if(pObj == NULL){
		return ret;
	}
	ret=RET_OK;
	if(OnOff == 0){
		pObj->stage =YM_S_IDLE;
	}
	else{
		pObj->stage =YM_S_START;
	}
	return ret;
}

#endif

#ifdef CFG_YMODEM_SLAVE
static int YMSlave_SendCode(tYM_Slave* pObj,uint8_t Val){
	// 
	int ret=RET_ERR;
	if(pObj == NULL) 		{  LLOG_ERR(); return ret; }
	if(pObj->pSend == NULL)	{  return ret; }
	
	ret=pObj->pSend(1,&Val);
	return ret;
}

static int YModem_CheckAllNull(uint16_t Len,uint8_t* pPkg){
	int ret=RET_OK;
	int i;
	for(i=0;i<Len;i++){
		if(pPkg[i] != 0x00){
			ret=RET_ERR;
		}
	}
	return ret;
}

int SM_YModem(tYM_Slave *pObj,uint16_t Len ,uint8_t *pPkg){
	// 耦合 ： 周期处理部分逻辑 以及 数据包驱动部分逻辑
	int ret = RET_ERR;
	uint8_t State;
	uint8_t RCmd;
	
	if(pObj == NULL)			{ LLOG_ERR(); return ret;}
	
	if( (Len > 0) && (pPkg == NULL)) { return ret;}
	
	State = pObj->stage;
	if( State == YM_S_IDLE)		{ return ret; }
	
	if(State > 0){
		if(Check_TimeOut(pObj->MarkTick,10000) == RET_OT){	// PkgToPkg TimeOut
			pObj->stage = YM_S_EXIT;LLOG_ERR();
		}
	}
	
	if(pPkg != NULL && Len > 0){
		RCmd = pPkg[0];
	}
	switch(State){
		case 0:{ // init 
			pObj->PkgSnCnt 		= 0;
			pObj->WaitSecCnt 	= CFG_YMS_WAIT_SEC;
			pObj->MarkTick 		= 0;
			pObj->RemainNum     = 0;
			
			pObj->MarkTick = GetNowTick();
			
			if((pObj->pProcDataPkg == NULL) || (pObj->pSend == NULL)){
				State = YM_S_IDLE;LLOG_ERR();
			}
			//else{
				State = 1;LLOG_MARK();
				// State++;LLOG_MARK();
			// }
			
		}break;
		case 1:{ // RYM_STAGE_ESTABLISHING
			if(Check_TimeOut(pObj->MarkTick,1000) == RET_OT){
				pObj->MarkTick = GetNowTick();
				// TODO 
				YMSlave_SendCode(pObj,RYM_CODE_C);
				pObj->WaitSecCnt--;
				if(pObj->WaitSecCnt == 0){
					State = YM_S_EXIT; LLOG_ERR();
				}
			}
			
			if(Len == 0){  break; } // 
			
			pObj->MarkTick = GetNowTick();
			LLOG_MARK();
 
			if( (RCmd ==RYM_CODE_SOH)\
			&& 	(pPkg[1]==pObj->PkgSnCnt) && (pPkg[2]==0xFF)){
				
				Len = RYM_SOH_DATA_SZ; // LLOG_MARK();
				if(pObj->pProcBeginPkg != NULL){
					ret = pObj->pProcBeginPkg(pObj,Len,pPkg);
				}
				else{
					ret == RET_ERR;
				}
				if((ret == RET_OK)&&(pObj->RemainNum > 0)){
					// TODO: Ack 
					pObj->PkgSnCnt++;
					YMSlave_SendCode(pObj,RYM_CODE_ACK);LLOG_MARK();
					YMSlave_SendCode(pObj,RYM_CODE_C);
					State++;
				}
				else{
					State=YM_S_EXIT;LLOG_ERR();
				}
			}
			else{
				State=YM_S_EXIT;LLOG_ERR();
			}
		}break;
		case 2:	 // RYM_STAGE_ESTABLISHED
		case 3:{ // RYM_STAGE_TRANSMITTING
			if (Len == 0) { return ret; }

			pObj->MarkTick = GetNowTick();

			if( State == 2){ 
				State++;
			}
	
			if (pPkg[1] == pObj->PkgSnCnt) { // Check Sn
				
				if(RCmd == RYM_CODE_SOH){
					Len = RYM_SOH_DATA_SZ;
				}
				else if(RCmd == RYM_CODE_STX){ 
					Len = RYM_STX_DATA_SZ;
				}
				else{
					State = YM_S_EXIT;	LLOG_ERR();				
					break;
				}
				
				if(Len >= pObj->RemainNum){	// Get Last Data Pkg
					Len = pObj->RemainNum;
					State++;
				}
				
				#if RYM_RPKG_SN_SHOW_OC
				LLOG("#D Len[%d] Sn[%d]:\n",Len,pPkg[1]);
				#endif 
				if(pObj->pProcDataPkg != NULL){
					ret = pObj->pProcDataPkg(pObj,Len,&pPkg[3]);
				}
				else{
					ret = RET_ERR;
				}
				
				if((ret == RET_OK)&&(pObj->RemainNum > 0)){
					pObj->RemainNum-=Len;
					pObj->PkgSnCnt++;
					YMSlave_SendCode(pObj,RYM_CODE_ACK);
				}
				else{
					LLOG("#E RemainNum[%d]\n",pObj->RemainNum);
					State=YM_S_EXIT; LLOG_ERR();
				}
			}
			else{
				State=YM_S_EXIT;	LLOG_ERR();			
			}
		}break;
		case 4:{ // RYM_STAGE_FINISHING 1

			if (Len == 0) { return ret; }

			pObj->MarkTick = GetNowTick();
			
			if( RCmd == RYM_CODE_EOT ){
				State++;
				YMSlave_SendCode(pObj,RYM_CODE_NAK);
			}
			else{
				State=YM_S_EXIT; LLOG_ERR();
			}
		}break;
		case 5:{ // RYM_STAGE_FINISHING 2
		
			if (Len == 0) { return ret; }

			pObj->MarkTick = GetNowTick();
			
			if( RCmd == RYM_CODE_EOT ){
				State++;
				YMSlave_SendCode(pObj,RYM_CODE_ACK);
				YMSlave_SendCode(pObj,RYM_CODE_C);
			}
			else{
				State=YM_S_EXIT; LLOG_ERR();			
			}
		}break;		
		case 6:{ // RYM_STAGE_FINISHING 3
			if (Len == 0) { return ret; }

			pObj->MarkTick = GetNowTick();
			
			if((RCmd == RYM_CODE_SOH) && (pPkg[1]==0x00) && (pPkg[2]==0xFF)){
				
				Len = RYM_SOH_DATA_SZ;
				ret = YModem_CheckAllNull(Len,&pPkg[3]);
				if(pObj->pProcEndPkg != NULL){
					pObj->pProcEndPkg(pObj,Len,pPkg);
				}
				YMSlave_SendCode(pObj,RYM_CODE_ACK);
				State++;
			}			
		}break;
		case 7:{ // RYM_STAGE_FINISHED
			State = YM_S_IDLE;LLOG_MARK();
			LLOG("FileTrans OK \n");
		}break;
		default:{
			State = YM_S_EXIT;LLOG_ERR();
		}
	}
	
	// 
	if(State == YM_S_EXIT){// Err Process
		// TODO: 结束文件传输 
		YMSlave_SendCode(pObj,RYM_CODE_CAN);	
		if(pObj->pExit != NULL){
			pObj->pExit(pObj,0,NULL);
		}
		State = YM_S_IDLE;LLOG_MARK();
	}
	
	pObj->stage=State;
	return ret;
}


#endif

